// PF9 button toggles LED2 (PF10) via libgpiod
// Added: MQTT subscribe to receive commands and control LED2
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <gpiod.h>
#include <limits.h>
#include <stdlib.h>
#include <signal.h>
#include "mqtt_client.h"
#include <time.h>
#include "gpio.h"
#include "app_config.h"
#include "led_gpio.h"
#include "sensor.h"
#include "adc.h"
#include "digital.h"
#include "led_sysfs.h"
#include "beeper.h"
#if defined(__has_include)
#  if __has_include(<cjson/cJSON.h>)
#    include <cjson/cJSON.h>
#  elif __has_include(<cJSON.h>)
#    include <cJSON.h>
#  else
#    error "cJSON header not found; install libcjson-dev or add cJSON to project"
#  endif
#else
#    include <cjson/cJSON.h>
#endif
#include "fan.h"

// LED上下文结构体定义
struct LedCtx {
    struct gpiod_line *line; // 可选：GPIO 控制（若未解绑 LED，可为空）
    int active_low;
    const char *sysfs_name;  // LED class 名称（/sys/class/leds/<name>/brightness）
    int *led_state; // logical state pointer
};

// 前向声明函数
static void set_led_from_logical(struct LedCtx *ctx, int logical);

static const char *LED_SYSFS_NAME = APPCFG_DEFAULT_LED_SYSFS_NAME; // e.g. "led2"
static volatile int g_running = 1;
static volatile int g_alarm_on = 0;   // 报警器开启（周期鸣叫）
static volatile int g_fan_on = -1;    // 风扇状态（-1 未知，0 关，1 开）

static void handle_sigint(int sig)
{
    (void)sig;
    g_running = 0;
}

// Wrapper to hold LED ctx via mqtt client user data
struct MqttCtx {
    const char *topic;
    struct LedCtx *led;
    mqtt_client_t *client;
    const char *pub_topic;
    int device_id;
};

// forward decl for use in callbacks
static void set_led_from_logical(struct LedCtx *ctx, int logical);

static void on_connect_mqtt(int rc, void *userdata)
{
    struct MqttCtx *mctx = (struct MqttCtx *)userdata;
    const char *topic = mctx ? mctx->topic : NULL;
    if (rc == 0 && topic) {
        fprintf(stdout, "MQTT connected. Subscribing: %s\n", topic);
        if (mctx && mctx->client) {
            // 按需求使用 QoS 0 订阅
            if (mqtt_client_subscribe(mctx->client, topic, 0) != 0) {
                fprintf(stderr, "Subscribe failed for %s\n", topic);
            }
        }
    } else if (rc != 0) {
        fprintf(stderr, "MQTT connect failed: rc=%d\n", rc);
    }
}

static void mqtt_on_message_cb(const char *topic, const void *payload, int payloadlen, void *userdata)
{
    (void)topic;
    struct MqttCtx *mctx = (struct MqttCtx *)userdata;
    struct LedCtx *led = mctx ? mctx->led : NULL;

    if (!(payload && payloadlen > 0)) return;
    char buf[256];
    size_t n = (size_t)payloadlen;
    if (n >= sizeof(buf)) n = sizeof(buf) - 1;
    memcpy(buf, payload, n);
    buf[n] = '\0';

    // Try JSON then plaintext
    cJSON *root = cJSON_Parse(buf);
    if (root) {
        // optional device id filtering
        int id_ok = 1;
        cJSON *idv = cJSON_GetObjectItemCaseSensitive(root, "id");
        if (idv && cJSON_IsNumber(idv)) {
            int rid = (int)(idv->valuedouble);
            if (mctx && rid != mctx->device_id) { id_ok = 0; fprintf(stderr, "Ignore command: id=%d expect=%d\n", rid, mctx->device_id); }
        }

        if (!id_ok) { cJSON_Delete(root); return; }

        cJSON *lamp = cJSON_GetObjectItemCaseSensitive(root, "lamp");
        if (lamp && (cJSON_IsBool(lamp) || cJSON_IsNumber(lamp))) {
            int logical = cJSON_IsBool(lamp) ? cJSON_IsTrue(lamp) : (lamp->valuedouble != 0);
            if (led) {
                set_led_from_logical(led, logical);
                printf("MQTT JSON set lamp -> LED=%s\n", logical ? "ON" : "OFF");
            }
        }
        cJSON *fan = cJSON_GetObjectItemCaseSensitive(root, "fan");
        if (fan) {
            int rc = 0;
            if (cJSON_IsBool(fan)) { rc = fan_set_on(cJSON_IsTrue(fan)); g_fan_on = cJSON_IsTrue(fan) ? 1 : 0; }
            else if (cJSON_IsNumber(fan)) {
                int percent = (int)(fan->valuedouble);
                if (percent < 0) percent = 0; if (percent > 100) percent = 100;
                rc = fan_set_speed(percent);
                g_fan_on = (percent > 0) ? 1 : 0;
            }
            if (rc != 0) fprintf(stderr, "MQTT JSON control fan failed\n");
        }

        // 报警器：两种交互
        // 1) {"alarm":bool,"id":int} 开/关（开启后周期鸣叫，直到关闭）
        // 2) {"beep":number(ms)|{"dur":ms,"freq":hz},"id":int} 单次短鸣
        cJSON *alarm = cJSON_GetObjectItemCaseSensitive(root, "alarm");
        if (alarm && cJSON_IsBool(alarm)) {
            g_alarm_on = cJSON_IsTrue(alarm) ? 1 : 0;
            if (!g_alarm_on) beeper_off();
        }

        cJSON *beep = cJSON_GetObjectItemCaseSensitive(root, "beep");
        if (beep) {
            if (cJSON_IsNumber(beep)) {
                int ms = (int)(beep->valuedouble);
                if (ms < 10) ms = 10; if (ms > 5000) ms = 5000;
                beeper_click(ms);
            } else if (cJSON_IsObject(beep)) {
                int ms = 100, freq = 2000;
                cJSON *dur = cJSON_GetObjectItemCaseSensitive(beep, "dur");
                cJSON *fq  = cJSON_GetObjectItemCaseSensitive(beep, "freq");
                if (dur && cJSON_IsNumber(dur)) ms = (int)(dur->valuedouble);
                if (fq && cJSON_IsNumber(fq)) freq = (int)(fq->valuedouble);
                if (ms < 10) ms = 10; if (ms > 5000) ms = 5000;
                if (freq < 100) freq = 100; if (freq > 10000) freq = 10000;
                beeper_beep(freq, ms);
            }
        }
        cJSON_Delete(root);
        // Echo original JSON to publish topic if configured (QoS1, wait ACK)
        if (mctx && mctx->client && mctx->pub_topic) {
            int rc = mqtt_client_publish_sync(mctx->client, mctx->pub_topic, buf, (int)strlen(buf), 1, 0, 3000);
            if (rc != 0) fprintf(stderr, "PUB ack failed: %d\n", rc);
        }
        return;
    }

    int new_state = -2; // -2 = no change
    if (strcasecmp(buf, "on") == 0 || strcmp(buf, "1") == 0) new_state = 1;
    else if (strcasecmp(buf, "off") == 0 || strcmp(buf, "0") == 0) new_state = 0;
    else if (strcasecmp(buf, "toggle") == 0) new_state = -1;

    if (new_state != -2 && led) {
        int logical = (led->led_state && *led->led_state) ? 1 : 0;
        if (new_state == -1) logical = !logical; else logical = new_state;
        set_led_from_logical(led, logical);
        printf("MQTT set LED=%s\n", logical ? "ON" : "OFF");
    }
}

#if 0
static void on_connect(struct mosquitto *mosq, void *userdata, int rc)
{
    struct MqttCtx *mctx = (struct MqttCtx *)userdata;
    const char *topic = mctx ? mctx->topic : NULL;
    if (rc == 0 && topic) {
        fprintf(stdout, "MQTT connected. Subscribing: %s\n", topic);
        int ret = mosquitto_subscribe(mosq, NULL, topic, 1);
        if (ret != MOSQ_ERR_SUCCESS) {
            fprintf(stderr, "Subscribe failed: %s\n", mosquitto_strerror(ret));
        }
    } else if (rc != 0) {
        fprintf(stderr, "MQTT connect failed: rc=%d (%s)\n", rc, mosquitto_strerror(rc));
    }
}

static void mqtt_on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg)
{
    (void)mosq;
    struct MqttCtx *mctx = (struct MqttCtx *)userdata;
    struct LedCtx *led = mctx ? mctx->led : NULL;

    // Copy payload to string buffer
    if (!(msg->payload && msg->payloadlen > 0)) return;
    char buf[256];
    size_t n = (size_t)msg->payloadlen;
    if (n >= sizeof(buf)) n = sizeof(buf) - 1;
    memcpy(buf, msg->payload, n);
    buf[n] = '\0';

    // Try JSON first per your spec table
    cJSON *root = cJSON_Parse(buf);
    if (root) {
        // 鐢电伅: {"lamp":bool,"id":int}
        cJSON *lamp = cJSON_GetObjectItemCaseSensitive(root, "lamp");
        if (lamp && (cJSON_IsBool(lamp) || cJSON_IsNumber(lamp))) {
            int logical = cJSON_IsBool(lamp) ? cJSON_IsTrue(lamp) : (lamp->valuedouble != 0);
            if (led && led->line) {
                set_led_from_logical(led, logical);
                printf("MQTT JSON set lamp -> LED=%s\n", logical ? "ON" : "OFF");
            }
        }

        // 椋庢墖: {"fan":bool,"id":int} 鎴?{"fan":number(0..100)}
        cJSON *fan = cJSON_GetObjectItemCaseSensitive(root, "fan");
        if (fan) {
            int rc = 0;
            if (cJSON_IsBool(fan)) {
                rc = fan_set_on(cJSON_IsTrue(fan));
            } else if (cJSON_IsNumber(fan)) {
                int percent = (int)(fan->valuedouble);
                if (percent < 0) percent = 0; if (percent > 100) percent = 100;
                rc = fan_set_speed(percent);
            }
            if (rc != 0) fprintf(stderr, "MQTT JSON control fan failed\n");
        }

        cJSON_Delete(root);
        return;
    }

    // Fallback: accept plaintext on/off/toggle
    int new_state = -2; // -2 = no change
    if (strcasecmp(buf, "on") == 0 || strcmp(buf, "1") == 0) new_state = 1;
    else if (strcasecmp(buf, "off") == 0 || strcmp(buf, "0") == 0) new_state = 0;
    else if (strcasecmp(buf, "toggle") == 0) new_state = -1;

    if (new_state != -2 && led && led->line) {
        int logical = (led->led_state && *led->led_state) ? 1 : 0;
        if (new_state == -1) logical = !logical; else logical = new_state;
        set_led_from_logical(led, logical);
        printf("MQTT set LED=%s\n", logical ? "ON" : "OFF");
    }
}
#endif

int main(void)
{
    signal(SIGINT, handle_sigint);
    // KEY1: 默认 PF9（低有效）
    const char *env_led = getenv("LED_SYSFS_NAME");
    char led_name_buf[64];
    const char *led_name = (env_led && env_led[0]) ? env_led : LED_SYSFS_NAME;
    // 若默认不可控，则自动探测一个可用的 LED 名称
    if (led_sysfs_set(led_name, 0) != 0) {
        if (led_sysfs_autodetect(led_name_buf, sizeof(led_name_buf)) == 0) {
            led_name = led_name_buf;
        }
    }

    printf("Ready: press KEY1 to toggle LED via LED class (%s)\n", led_name);

    // Setup MQTT subscriber
    const char *mqtt_host = getenv("MQTT_HOST");
    if (!mqtt_host || !*mqtt_host) mqtt_host = APPCFG_DEFAULT_MQTT_HOST;
    int mqtt_port = APPCFG_DEFAULT_MQTT_PORT;
    if (getenv("MQTT_PORT") && *getenv("MQTT_PORT")) mqtt_port = atoi(getenv("MQTT_PORT"));
    const char *mqtt_topic = getenv("MQTT_TOPIC");
    if (!mqtt_topic || !*mqtt_topic) mqtt_topic = APPCFG_DEFAULT_SUB_TOPIC; // 订阅
    const char *pub_topic = getenv("MQTT_PUB_TOPIC");
    if (!pub_topic || !*pub_topic) pub_topic = APPCFG_DEFAULT_PUB_TOPIC;   // 发布
    int pub_interval = 5; // seconds
    if (getenv("MQTT_PUB_INTERVAL") && *getenv("MQTT_PUB_INTERVAL")) pub_interval = atoi(getenv("MQTT_PUB_INTERVAL"));
    if (pub_interval < 1) pub_interval = 1;
    int device_id = APPCFG_DEFAULT_DEVICE_ID;
    if (getenv("DEVICE_ID") && *getenv("DEVICE_ID")) device_id = atoi(getenv("DEVICE_ID"));
    // 火焰->报警 联动开关（默认开启，设置 FLAME_ALARM_ENABLE=0 可关闭）
    int flame_alarm_enable = 1; const char *fae = getenv("FLAME_ALARM_ENABLE");
    if (fae && *fae) flame_alarm_enable = atoi(fae) ? 1 : 0;

    struct LedCtx ledctx = { .line = NULL, .active_low = 0, .sysfs_name = led_name, .led_state = NULL };
    int led_state = 0; // logical OFF
    ledctx.led_state = &led_state;

    #if 0
    mosquitto_lib_init();
    struct MqttCtx mctx = { .topic = mqtt_topic, .led = &ledctx };
    const char *mqtt_client_id = getenv("MQTT_CLIENT_ID");
    struct mosquitto *mosq = mosquitto_new((mqtt_client_id && *mqtt_client_id) ? mqtt_client_id : NULL, true, &mctx);
    if (!mosq) {
        fprintf(stderr, "mosquitto_new OOM\n");
    } else {
        // 鍗忚鐗堟湰璁剧疆涓?3.1.1锛堜笌閰嶇疆涓€鑷达級
#ifdef MOSQ_OPT_PROTOCOL_VERSION
        mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V311);
#endif

        // 璁よ瘉锛堢敤鎴峰悕/瀵嗙爜锛?        const char *u = getenv("MQTT_USERNAME");
        const char *p = getenv("MQTT_PASSWORD");
        if (u && *u) {
            mosquitto_username_pw_set(mosq, u, (p && *p) ? p : NULL);
        }

        // TLS 鏀寔锛氬綋 MQTT_TLS_ENABLE=1 鏃跺惎鐢?        const char *tls_enable = getenv("MQTT_TLS_ENABLE");
        if (tls_enable && atoi(tls_enable) == 1) {
            const char *cafile = getenv("MQTT_CAFILE");
            const char *capath = getenv("MQTT_CAPATH");
            if (mosquitto_tls_set(mosq, cafile, capath, NULL, NULL, NULL) != MOSQ_ERR_SUCCESS) {
                fprintf(stderr, "WARN: TLS set failed (check MQTT_CAFILE/MQTT_CAPATH)\n");
            }
            const char *insecure = getenv("MQTT_INSECURE");
            if (insecure && atoi(insecure) == 1) {
                mosquitto_tls_insecure_set(mosq, true);
            }
        }

        mosquitto_connect_callback_set(mosq, on_connect);
        mosquitto_message_callback_set(mosq, mqtt_on_message);
        mosquitto_reconnect_delay_set(mosq, 2, 10, true);
        int mret = mosquitto_connect(mosq, mqtt_host, mqtt_port, 60);
        if (mret != MOSQ_ERR_SUCCESS) {
            fprintf(stderr, "MQTT connect error: %s\n", mosquitto_strerror(mret));
        } else {
            mosquitto_loop_start(mosq);
            printf("MQTT: broker=%s:%d topic=%s\n", mqtt_host, mqtt_port, mqtt_topic);
        }
    }
    #endif

    // New MQTT client (modular wrapper)
    struct MqttCtx mctx = { .topic = mqtt_topic, .led = &ledctx, .client = NULL, .pub_topic = pub_topic, .device_id = device_id };
    mqtt_config_t mcfg; mqtt_config_from_env(&mcfg);
    // Built-in defaults when env not provided
    if (!(getenv("MQTT_HOST") && *getenv("MQTT_HOST"))) mcfg.host = APPCFG_DEFAULT_MQTT_HOST; else mcfg.host = getenv("MQTT_HOST");
    if (!(getenv("MQTT_PORT") && *getenv("MQTT_PORT"))) mcfg.port = APPCFG_DEFAULT_MQTT_PORT; else mcfg.port = atoi(getenv("MQTT_PORT"));
    if (!(getenv("MQTT_WS_ENABLE") && *getenv("MQTT_WS_ENABLE"))) mcfg.ws_enable = APPCFG_DEFAULT_WS_ENABLE ? true : false;
    if (!(getenv("MQTT_TLS_ENABLE") && *getenv("MQTT_TLS_ENABLE"))) mcfg.tls_enable = APPCFG_DEFAULT_TLS_ENABLE ? true : false;
    if (mcfg.ws_enable && !(getenv("MQTT_WS_PATH") && *getenv("MQTT_WS_PATH"))) mcfg.ws_path = APPCFG_DEFAULT_WS_PATH;
    mqtt_client_t *mqtt = mqtt_client_new(&mcfg, &mctx);
    mctx.client = mqtt;
    if (!mqtt) {
        fprintf(stderr, "mqtt_client_new failed\n");
    } else {
        mqtt_client_set_message_cb(mqtt, mqtt_on_message_cb);
        mqtt_client_set_connect_cb(mqtt, on_connect_mqtt);
        int cret = mqtt_client_connect(mqtt);
        if (cret != 0) {
            fprintf(stderr, "MQTT connect error: %s (host=%s port=%d%s)\n", mqtt_errstr(cret), mcfg.host, mcfg.port, mcfg.ws_enable?" ws":"");
        } else {
            mqtt_client_start(mqtt);
            printf("MQTT: broker=%s:%d%s sub=%s pub=%s\n", mcfg.host, mcfg.port, mqtt_client_uses_ws(mqtt)?" ws":"", mqtt_topic, pub_topic);
        }
    }

    time_t last_pub = 0;
    time_t last_alarm = 0; // 周期报警节拍
    while (g_running) {
        int pressed = 0;
        if (read_key(1, &pressed) != 0) {
            fprintf(stderr, "read KEY1 failed\n");
            usleep(100 * 1000);
            continue;
        }

        if (pressed) {
            // debounce
            usleep(20 * 1000);
            if (read_key(1, &pressed) == 0 && pressed) {
                // toggle LED logical state
                led_state = !led_state;
                if (led_sysfs_set(led_name, led_state) != 0) perror("set LED sysfs");
                    printf("LED=%s\n", led_state ? "ON" : "OFF");

                // wait release
                do {
                    usleep(10 * 1000);
                    if (read_key(1, &pressed) != 0) {
                        break;
                    }
                } while (pressed);
            }
        }

        // 周期报警（开启后每 1s 响 120ms）
        if (g_alarm_on) {
            time_t nowb = time(NULL);
            if (nowb - last_alarm >= 1) { beeper_beep(2000, 120); last_alarm = nowb; }
        }

        // telemetry publish
        time_t now = time(NULL);
        if (mctx.client && now - last_pub >= pub_interval) {
            float t = 0.0f, h = 0.0f;
            int ok_t = (read_temperature(&t) == 0);
            int ok_h = (read_humidity(&h) == 0);
            float lux = 0.0f; int ok_l = (read_light(&lux) == 0);
            // 数字量
            int ir_valid = 0, ir = 0; if (read_infrared(&ir) == 0) ir_valid = 1;
            int photo_valid = 0, photo = 0; if (read_photo_switch(&photo) == 0) photo_valid = 1;
            int flame_valid = 0, flame = 0; if (read_flame(&flame) == 0) flame_valid = 1;
            if (flame_alarm_enable && flame_valid && flame && !g_alarm_on) { g_alarm_on = 1; printf("ALARM: flame detected -> alarm on\n"); }
            // 其他按键：key2/key3（均低有效）
            int key2 = 0, key3 = 0; int key2_ok = (read_key(2, &key2) == 0); int key3_ok = (read_key(3, &key3) == 0);
            // ADC：电压/电流
            float volt = 0.0f, amp = 0.0f; int ok_v = (read_voltage(&volt) == 0); int ok_a = (read_current(&amp) == 0);
            cJSON *root = cJSON_CreateObject();
            if (ok_t) cJSON_AddNumberToObject(root, "tem", t);
            if (ok_h) cJSON_AddNumberToObject(root, "hum", h);
            if (ok_l) cJSON_AddNumberToObject(root, "light", lux);
            // doorLock: use key pressed state as a boolean (true when pressed)
            cJSON_AddBoolToObject(root, "doorLock", pressed ? 1 : 0);
            // 可同时上报 key1/2/3，便于调试
            cJSON_AddBoolToObject(root, "key1", pressed ? 1 : 0);
            if (key2_ok) cJSON_AddBoolToObject(root, "key2", key2 ? 1 : 0);
            if (key3_ok) cJSON_AddBoolToObject(root, "key3", key3 ? 1 : 0);
            if (ir_valid) cJSON_AddBoolToObject(root, "infrared", ir ? 1 : 0);
            if (photo_valid) cJSON_AddBoolToObject(root, "photo", photo ? 1 : 0);
            if (flame_valid) cJSON_AddBoolToObject(root, "flame", flame ? 1 : 0);
            if (ok_v) cJSON_AddNumberToObject(root, "voltage", volt);
            if (ok_a) cJSON_AddNumberToObject(root, "current", amp);
            // 可选：上报 LED 当前状态，字段名与控制保持一致（lamp）
            if (led_state == 0 || led_state == 1) cJSON_AddBoolToObject(root, "lamp", led_state ? 1 : 0);
            if (g_fan_on != -1) cJSON_AddBoolToObject(root, "fan", g_fan_on ? 1 : 0);
            cJSON_AddBoolToObject(root, "alarm", g_alarm_on ? 1 : 0);
            cJSON_AddNumberToObject(root, "id", device_id);
            char *json = cJSON_PrintUnformatted(root);
            if (json) {
                mqtt_client_publish(mctx.client, pub_topic, json, (int)strlen(json), 0, 0);
                printf("PUB %s -> %s\n", pub_topic, json);
                cJSON_free(json);
            }
            cJSON_Delete(root);
            last_pub = now;
        }

        usleep(10 * 1000);
    }

    if (mctx.client) {
        mqtt_client_disconnect(mctx.client);
        mqtt_client_stop(mctx.client);
        mqtt_client_destroy(mctx.client);
        mctx.client = NULL;
    }
    return 0;
}

static void set_led_from_logical(struct LedCtx *ctx, int logical)
{
    int phys = ctx->active_low ? !logical : logical;
    if (ctx->line) {
        (void)gpiod_line_set_value(ctx->line, phys);
    }
    if (ctx->sysfs_name && *ctx->sysfs_name) {
        (void)led_sysfs_set(ctx->sysfs_name, logical ? 1 : 0);
    }
    if (ctx->led_state) *ctx->led_state = logical;
}
